package edu.gatech.cc.liam.core.rl.models;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;


/***
 * 
 * @author Liam
 *
 * @param <T>
 * 
 * Returns a generic iterator over the joint of the constructor input sets
 */
public class JointIterator<T> implements Iterator<ArrayList<T>>, Iterable<ArrayList<T>> {

	private ArrayList<T> lastJointReturned;
	private Iterator<? extends T>[] currentIterators;
	private int[] currentNums;
	private List<? extends Iterable<? extends T>> theSets;
	//int numPlayers;
	
	@SuppressWarnings("unchecked")
	public JointIterator(List<? extends Iterable<? extends T>> theSets) {
		super();
		this.theSets = theSets;
		int numSets = theSets.size();
		currentIterators = new Iterator[numSets];
		for(int i=0; i<numSets; i++) {
			currentIterators[i] = theSets.get(i).iterator();
		}
		lastJointReturned = new ArrayList<T>(numSets);//(T[]) Array.newInstance(c,numSets);//(T[]) new Object[numSets];
		for(int i=0; i<numSets; i++) {
			lastJointReturned.add(null);
		}
		for(int i=1; i<numSets; i++) {
			lastJointReturned.set(i, currentIterators[i].next());
		}
		currentNums = new int[theSets.size()];
		Arrays.fill(currentNums, 0);
		//the first action doesn't get set until the first call to next()
		currentNums[0] = -1;
	}
	public JointIterator(Iterable<? extends T> ... theSets) {
		this(Arrays.asList(theSets));
	}

	public static JointIterator<Integer> cartesianIndecies(int[] setSizes) {
		ArrayList<ArrayList<Integer>> sets = new ArrayList<ArrayList<Integer>>(setSizes.length);
		for(int i=0; i<setSizes.length; i++) {
			ArrayList<Integer> ints = new ArrayList<Integer>(setSizes[i]);
			sets.add(ints);
			for(int j=0; j<setSizes[i]; j++)
				ints.set(j, j);
		}
		return new JointIterator<Integer>(sets);
	}
	
	public static int getJointIndex(int[] fieldSizes, int[] indices) {
		int indexVal = indices[0];
		int multiplier = 1;
		for(int i = 1; i < indices.length; i++) {
			assert(indices[i] < fieldSizes[i]);
			multiplier *= fieldSizes[i-1];
			indexVal += indices[i] * multiplier;
		}
		return indexVal;
	}
	
	public static int[] getJoint(int[] fieldSizes, int jointIndex) {
		int[] indices = new int[fieldSizes.length];
		indices[0] = jointIndex;
		for(int i = 0; i < fieldSizes.length-1; i++) {
			indices[i+1] = indices[i] / fieldSizes[i];
			indices[i] = indices[i] % fieldSizes[i];
		}
		return indices;
	}
	
	public static Object[] getObjects(List<? extends Object>[] obj, int[] ind) {
		Object[] r = new Object[obj.length];
		for(int i=0; i<ind.length; i++) {
			r[i] = obj[i].get(ind[i]);
		}
		return r;
	}
	
	protected JointIterator() {
		super();
	}
 
	@Override
	public boolean hasNext() {
		for(int i=0; i<theSets.size(); i++){
			if(this.currentIterators[i].hasNext()){
				return true;
			}
		}
		return false;
	}

	@SuppressWarnings("unchecked")
	@Override
	public ArrayList<T> next() {
		for(int i=0; i<theSets.size(); i++) {
			if(currentIterators[i].hasNext()) {
				lastJointReturned.set(i, currentIterators[i].next());
				currentNums[i]++;
				break;
			} else {
				currentIterators[i] = theSets.get(i).iterator();
				lastJointReturned.set(i, currentIterators[i].next());
				currentNums[i]=0;
			}
		}
		return (ArrayList<T>) lastJointReturned.clone();
	}
	
	public int[] getCurrentNums() {
		return this.currentNums;
	}

	@Override
	public void remove() {
		throw new UnsupportedOperationException();			
	}

	@Override
	public Iterator<ArrayList<T>> iterator() {
		return this;
	}
	
	ArrayList<T> makeArrayListFromArray(T[] theArray) {
		return new ArrayList<T>(Arrays.asList(theArray));
	}
	
}
